package com.hup.minicontroller.common.netty.handler;

import android.util.Log;

import com.hup.minicontroller.common.netty.model.HeartbeatPacket;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.Getter;

/**
 * 心跳包处理器/模块;<pre>
 * 功能逻辑:
 * .连接建立后,(server和client各自都会)持续监听'有无收到远端socket发送数据',若 {@link #readerIdleTimeSeconds}秒内无收到任何数据,则判断连接丢失
 * .对于发送心跳包 {@link HeartbeatPacket},则分为[主动发送方]和[被动回复方], 用 {@link #isActiveSender}进行配置
 * .[主动发送方]:连接建立后,定时主动发送 {@link HeartbeatPacket}
 * .[被动回复方]:只有在收到远端的 {@link HeartbeatPacket}后才回复 {@link HeartbeatPacket}
 * .至此就建立了通信双方的心跳机制
 * 使用方式:
 * .调用初始化方法: {@link #initHeartBeat(boolean, IdleCallback)};一般情况下,每个程序都应仅执行一次,来配置当前程序是[主动发送方]还是[被动回复方]
 * .建议Client是[主动发送方],server是[被动回复方]
 * .建立连接时({@link ChannelInitializer}), 调用 {@link #addHeartbeatHandler(ChannelPipeline)}
 * 其他说明:
 * .使用 {@link IdleStateHandler}实现[持续监听远端有无消息]
 * .使用 {@link #heartbeatSenderTask}实现[主动发送方],代码参考自 {@link IdleStateHandler}的AbstractIdleTask,但没做冗余的初始化和销毁判断
 * .{@link HeartbeatHandler}不能是 {@link Sharable},每个连接都是独立监听和计时的
 *
 * @author hugan
 * @date 2021/5/12
 */
public class HeartbeatHandler extends IdleStateHandler {
    private static final String TAG = HeartbeatHandler.class.getSimpleName();

    private static boolean isActiveSender;
    private static IdleCallback idleCallback;
    /**
     * n秒内无读取到数据,则判断为连接丢失
     */
    @Getter
    private static int readerIdleTimeSeconds = 10;

    private ScheduledFuture<?> heartbeatSenderTask;

    private HeartbeatHandler() {
        //其他为0的值代表[不加入监听功能]
        super(readerIdleTimeSeconds, 0, 0);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        if (isActiveSender) {
            //仅连接成功后初始化,没在handlerAdded,channelRegistered做三次initialize
            Log.d(TAG, "channelActive: 初始化sender");
            heartbeatSenderTask = ctx.executor().schedule(new HeartbeatActiveSender(ctx),
                    HeartbeatActiveSender.heartbeatInterval, TimeUnit.SECONDS);
        }
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //紧连接断开时销毁,没在handlerRemoved做双重destroy
        if (heartbeatSenderTask != null) {
            Log.d(TAG, "channelInactive: 销毁sender");
            heartbeatSenderTask.cancel(false);
            heartbeatSenderTask = null;
        }
        super.channelInactive(ctx);
    }

    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
        if (idleCallback != null) idleCallback.onChannelIdle(ctx, e);
    }

    /**
     * {@link #initHeartBeat(boolean, IdleCallback, int)}
     */
    public static void initHeartBeat(boolean isActiveSender, IdleCallback idleCallback) {
        initHeartBeat(isActiveSender, idleCallback, readerIdleTimeSeconds);
    }

    /**
     * 初始化心跳模块; 说明: {@link HeartbeatHandler}
     *
     * @param isActiveSender 建议Client是[主动发送方],server是[被动回复方]
     */
    public static void initHeartBeat(boolean isActiveSender, IdleCallback idleCallback, int readerIdleTimeSeconds) {
        HeartbeatHandler.isActiveSender = isActiveSender;
        HeartbeatHandler.idleCallback = idleCallback;
        HeartbeatHandler.readerIdleTimeSeconds = readerIdleTimeSeconds;
        HeartbeatActiveSender.heartbeatInterval = (int) (readerIdleTimeSeconds * 0.5);
    }

    /**
     * {@link ChannelInitializer}时调用,添加心跳处理器;
     * 建议放在[登陆Handler]之后,[业务数据Handler]之前
     *
     * @param pipeline ch.pipeline()
     */
    public static void addHeartbeatHandler(ChannelPipeline pipeline) {
        pipeline.addLast(new HeartbeatHandler());
        /*
        虽然[主动发送方]不需要接收和处理HeartbeatPacket,但此处还是添加了处理器
        目的:避免HeartbeatPacket被传到pipeline后面的业务处理器中
         */
        pipeline.addLast(HeartbeatPassiveReceiver.INSTANCE);
    }

    public interface IdleCallback {
        /**
         * n秒内无收到任何远端消息时的心跳机制回调
         */
        default void onChannelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
            Log.i(TAG, "onChannelIdle: 无收到消息=" + readerIdleTimeSeconds + "秒,主动关闭连接");
            ctx.channel().close();
        }
    }

    /**
     * 主动发送心跳包的Sender
     */
    public static class HeartbeatActiveSender implements Runnable {
        private final ChannelHandlerContext ctx;
        /**
         * 心跳包发送间隔,小于readerIdleTimeSeconds
         */
        private static int heartbeatInterval = (int) (readerIdleTimeSeconds * 0.5);

        HeartbeatActiveSender(ChannelHandlerContext ctx) {
            this.ctx = ctx;
        }

        @Override
        public void run() {
            if (!ctx.channel().isOpen()) return;
            Log.d(TAG, "run: 发送心跳包");
            ctx.writeAndFlush(new HeartbeatPacket());
            ctx.executor().schedule(this, heartbeatInterval, TimeUnit.SECONDS);
        }
    }

    /**
     * 收到远端心跳包后,被动回复心跳包的Receiver和Sender
     */
    @Sharable
    public static class HeartbeatPassiveReceiver extends SimpleChannelInboundHandler<HeartbeatPacket> {
        /**
         * 单例,初始化时,注册 {@link HeartbeatPacket}的编解码器
         */
        public static final HeartbeatPassiveReceiver INSTANCE = new HeartbeatPassiveReceiver();

        private HeartbeatPassiveReceiver() {
            Log.d(TAG, "HeartbeatPassiveReceiver: 注册HeartbeatPacket的编解码器");
            DataCodecHandler.INSTANCE.registerType(HeartbeatPacket.class);
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, HeartbeatPacket msg) {
            if (!isActiveSender) {
                Log.d(TAG, "channelRead0: 收到并回复心跳包");
                ctx.writeAndFlush(new HeartbeatPacket());
            }
        }
    }

}
